using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BoldBrick.Core.Logging;
using BoldBrick.OneIntranet.HR.DataObjects;
using BoldBrick.OneIntranet.HR.DataObjects.ContentTypes;
using BoldBrick.OneIntranet.HR.DataObjects.Lists;
using BoldBrick.OneIntranet.HR.Properties;
using BoldBrick.SharePoint.CommonControls.DataObjects.Base;
using BoldBrick.SharePoint.CommonControls.Extensions;
using BoldBrick.SharePoint.CommonControls.Logging;
using Microsoft.SharePoint;
using BaseContentType = BoldBrick.SharePoint.CommonControls.DataObjects.Base.BaseContentType;
using BaseList = BoldBrick.SharePoint.CommonControls.DataObjects.Base.BaseList;
using System.Linq;

namespace BoldBrick.OneIntranet.HR.Features.Main
{
	/// <summary>
	/// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
	/// </summary>
	/// <remarks>
	/// The GUID attached to this class may be used during packaging and should not be modified.
	/// </remarks>
	[Guid("5f294fa7-4bbd-42a2-89a9-5912f4ac8495")]
	public class MainEventReceiver : SPFeatureReceiver
	{
		public override void FeatureActivated(SPFeatureReceiverProperties properties)
		{
			using (SPWeb webParent = properties.Feature.Parent as SPWeb)
			{
				if (webParent == null)
				{
					FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "Web not found in '{0}' feature.", properties.Feature.DefinitionId);
					return;
				}

				DataObjects.Features.WorkflowsFeature.TryActivate(webParent.Site);
				

				Guid webID = webParent.ID;
				Guid siteID = webParent.Site.ID;
				
				PerformWebAction(siteID, webID, web =>
				{
					// Create columns
					FeatureLogger.Write<MainEventReceiver>(LogLevel.Info, "Create columns.");
					DataObjects.Features.SiteColumnsFeature.ReactivateFeature(web);

					SetupColumns(web);

					// Create content types
					FeatureLogger.Write<MainEventReceiver>(LogLevel.Info, "Create content types.");
					CreateContentTypes(web);
				});

				PerformWebAction(siteID, webID, web => 
				{
					// Create lists
					SPList listTasks = EnsureList(web, Lists.TasksList, false);
					listTasks.ContentTypesEnabled = false;
					listTasks.Update();

					EnsureList(web, Lists.PositionsList, true);
					EnsureList(web, Lists.CandidatesList, true);
					EnsureList(web, Lists.WorkflowsList, true);
					EnsureList(web, Lists.WorkflowHistoryList, true);

					// Setup lookup fields
					SetupLookupFields(web);
				});
				
				// setup root folder content types
				PerformWebAction(siteID, webID, web =>
				{
					SetRootFolderContentType(web, Lists.CandidatesList, ContentTypes.CandidateContentType);
					SetRootFolderContentType(web, Lists.WorkflowsList, ContentTypes.WorkflowContentType);
				});

				// setup columns
				PerformWebAction(siteID, webID, web =>
				{
					HideColumn(web, ContentTypes.ResultContentType, 
						SharePoint.CommonControls.DataObjects.SiteColumns.TitleColumn,
						SiteColumns.CancelIfNoHireColumn);

					HideColumn(web, ContentTypes.CVDocumentContentType, 
						SharePoint.CommonControls.DataObjects.SiteColumns.TitleColumn);

					HideColumn(web, ContentTypes.WorkflowStepContentType, 
						SharePoint.CommonControls.DataObjects.SiteColumns.TitleColumn);
					
					HideColumn(web, ContentTypes.CandidateContentType, 
						SharePoint.CommonControls.DataObjects.SiteColumns.NameColumn);
				});
				PerformWebAction(siteID, webID, web =>
				{
					HideColumn(web, ContentTypes.FinalDecisionWorkflowStepContentType,
						SiteColumns.CancelIfNoHireColumn);
				});

				
			}
		}

		

		public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
		{
			base.FeatureDeactivating(properties);

			using (SPWeb webParent = properties.Feature.Parent as SPWeb)
			{
				if (webParent == null)
				{
					FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "Web not found in '{0}' feature.", properties.Feature.DefinitionId);
					return;
				}

				RemoveEvents(webParent, Lists.CandidatesList);
				RemoveEvents(webParent, Lists.WorkflowsList);
				
			}
		}

		/// <summary>
		/// Perform action on a new instance of the <see cref="SPWeb"/> object
		/// </summary>
		/// <param name="siteID">Site ID of the web</param>
		/// <param name="webID">Web ID of the web</param>
		/// <param name="webAction">Action to perform</param>
		private static void PerformWebAction(Guid siteID, Guid webID, WebAction webAction)
		{
			using (SPSite site = new SPSite(siteID))
			{
				using (SPWeb web = site.OpenWeb(webID))
				{
					webAction(web);
				}
			}
		}

		private delegate void WebAction(SPWeb web);

		

		#region ------ Lists Methods --------------------------------------------------------------

		/// <summary>
		/// Creates the list if this list is not created.
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list">The list to create</param>
		/// <param name="update">Set true to update changes in list immediately</param>
		/// <returns>Returns created or obtained list</returns>
		private static SPList EnsureList(SPWeb web, BaseList list, bool update)
		{
			FeatureLogger.Write<MainEventReceiver>(LogLevel.Info, "Ensuring '{0}' in '{1}' web.", list.GetListName(web.Language), web.ServerRelativeUrl);
			SPList spList = web.GetList(list);

			List<Guid> viewIdsToDelete = new List<Guid>();
			// create if list is not created
			if (spList == null)
			{
				spList = list.CreateList(web);
				// log error if list is not obtained
				if (spList == null)
				{
					FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "'{0}' list not found or created in '{1}' web.",
									list.GetListName(web.Language),
									web.ServerRelativeUrl);
					return null;
				}
				
				viewIdsToDelete = spList.Views.OfType<SPView>()
					.Select(v => v.ID)
					.ToList();
			}

			// update list
			list.UpdateContentTypes(web, spList);
			list.UpdateColumns(web, spList);
			list.UpdateViews(web, spList);
			list.UpdateList(web, spList);
			list.AddAllEventReceivers(web, spList);
			list.AssociatedWorkflows(web, spList);

			// if custom views are defined, delete old ones
			ViewsCollection viewsCollection = list.GetViews();
			if (viewsCollection != null && viewsCollection.Count > 0)
			{
				foreach (Guid vieID in viewIdsToDelete)
				{	
					spList.Views.Delete(vieID);
				}
			}

			if (update)
			{
				spList.Update();
			}

			return spList;
		}

		/// <summary>
		/// Sets content type for root folder of the list
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list">List to setup</param>
		/// <param name="contentType">Content type for root folder</param>
		private static void SetRootFolderContentType(SPWeb web, BaseList list, BaseContentType contentType)
		{
			FeatureLogger.Write<MainEventReceiver>(LogLevel.Info, "Setup root folder content types in '{0}' list", list.GetListName(web.Language));

			SPList spList = web.GetList(list);
			if (spList == null)
			{
				FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "'{0}' list not found in '{1}' web.", Lists.CandidatesList.GetListName(web.Language), web.ServerRelativeUrl);
				return;
			}
			SPContentType spContentType = spList.ContentTypes.GetChildContentType(contentType.ID);
			spList.RootFolder.UniqueContentTypeOrder = new List<SPContentType>
			                                            {
			                                                spContentType
			                                            };
			spList.RootFolder.Update();
		}

		/// <summary>
		/// Removes all events from list
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list">The list</param>
		private static void RemoveEvents(SPWeb web, BaseList list)
		{
			SPList spList = web.GetList(list);
			if (spList != null)
			{
				list.RemoveAllEventReceivers(web, spList);
			}
		}

		#endregion

		#region ------ Content Types --------------------------------------------------------------

		/// <summary>
		/// Creates all content types for HR solution
		/// </summary>
		/// <param name="web">Parent web of content types</param>
		private static void CreateContentTypes(SPWeb web)
		{
			CreateContentType(web, ContentTypes.PositionContentType);
			
			CreateContentType(web, ContentTypes.CVDocumentContentType);

			// Candidates content type
			CreateContentType(web, ContentTypes.CandidateContentType);

			// Workflow Content Type
			CreateContentType(web, ContentTypes.WorkflowContentType);
			
			
			// for each level of inheritance of content types the new web has to be opened
			// otherwise following error appear: The object has been updated by another user since it was last fetched. 
			// 1st level
			CreateContentType(web, ContentTypes.ResultContentType);
			CreateContentType(web, ContentTypes.WorkflowStepContentType);
			CreateContentType(web, ContentTypes.HRTaskContentType);

			// 2nd level
			using (SPWeb webLevel2 = web.Site.OpenWeb(web.ID))
			{
				// candidate content types
				CreateContentType(webLevel2, ContentTypes.TestResultContentType);
				CreateContentType(webLevel2, ContentTypes.InterviewResultContentType);
				CreateContentType(webLevel2, ContentTypes.ReferenceCallResultContentType);
				CreateContentType(webLevel2, ContentTypes.FinalDecisionResultContentType);
				CreateContentType(webLevel2, ContentTypes.ReviewResultContentType);
				CreateContentType(webLevel2, ContentTypes.CandidateDecisionResultContentType);
				CreateContentType(webLevel2, ContentTypes.ScheduleInterviewResultContentType);

				// workflow content types
				CreateContentType(webLevel2, ContentTypes.InterviewWorkflowStepContentType);
				CreateContentType(webLevel2, ContentTypes.TestWorkflowStepContentType);
				CreateContentType(webLevel2, ContentTypes.ReferenceCallWorkflowStepContentType);
				CreateContentType(webLevel2, ContentTypes.FinalDecisionWorkflowStepContentType);
				CreateContentType(webLevel2, ContentTypes.ReviewWorkflowStepContentType);
				CreateContentType(webLevel2, ContentTypes.ScheduleInterviewWorkflowStepContentType);

				// task content types
				CreateContentType(webLevel2, ContentTypes.InterviewTaskContentType);
				CreateContentType(webLevel2, ContentTypes.TestTaskContentType);
				CreateContentType(webLevel2, ContentTypes.ReferenceCallTaskContentType);
				CreateContentType(webLevel2, ContentTypes.FinalDecisionTaskContentType);
				CreateContentType(webLevel2, ContentTypes.ReviewTaskContentType);
				CreateContentType(webLevel2, ContentTypes.ScheduleInterviewTaskContentType);
			}

		}

		/// <summary>
		/// Creates spefified content type
		/// </summary>
		/// <param name="web">Parent web of the content type</param>
		/// <param name="contentType">Metadata object of the content type to create</param>
		private static void CreateContentType(SPWeb web, BaseContentType contentType)
		{
			contentType.Create(web);
			contentType.UpdateColumnOrder(web.ContentTypes);
		}

		private static void HideColumn(SPWeb web, BaseContentType contentType, params ListColumn[] columnsToHide)
		{
			SPContentType spContentType = web.ContentTypes[contentType.ID];
			if (spContentType == null)
			{
				FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "Content type '{0}' not found in '{1}' web.", contentType, web.ServerRelativeUrl);
				return;
			}
			foreach (ListColumn column in columnsToHide)
			{
				SPFieldLink fieldLinkTitle = spContentType.FieldLinks[column.InternalName];
				fieldLinkTitle.Hidden = true;
			}
			
			spContentType.Update(true);
		}

		#endregion

		#region ------ Columns --------------------------------------------------------------------

		/// <summary>
		/// Setups all lookup field in HR solution
		/// </summary>
		/// <param name="web">Parent web of fields</param>
		private static void SetupLookupFields(SPWeb web)
		{
			SetupLookupField(web, SiteColumns.PositionLookupColumn, Lists.PositionsList);
			SetupLookupField(web, SiteColumns.WorkflowPositionLookupColumn, Lists.PositionsList);
			SetupLookupField(web, SiteColumns.EmploymentTypesLookupColumn, Lists.EmploymentTypesList);
			SetupLookupField(web, SiteColumns.SourceLookupColumn, Lists.SourcesList);
			SetupLookupField(web, SiteColumns.SeniorityLevelLookupColumn, Lists.SeniorityLevelsList);
			SetupLookupField(web, SiteColumns.DocumentTypeLookupColumn, Lists.DocumentTypesList);
			SetupLookupField(web, SiteColumns.LanguageLookupColumn, Lists.LanguagesList);
			SetupLookupField(web, SiteColumns.TestTypeLookupColumn, Lists.TestTypesList);
		}

		/// <summary>
		/// Setups lookup field in the web
		/// </summary>
		/// <param name="web">Parent web of the field</param>
		/// <param name="listColumn">Metadata of the field</param>
		/// <param name="lookupList">Lookup list of the lookup field</param>
		private static void SetupLookupField(SPWeb web, ListColumn listColumn, BaseList lookupList)
		{
			FeatureLogger.Write<MainEventReceiver>(LogLevel.Info, "Setuping '{0}' lookup column to lookup in '{1}' list.", listColumn.InternalName, lookupList.GetListName(web.Language));
			SPList spLookupList = EnsureList(web, lookupList, true);
			if (spLookupList == null)
				return;

			SPFieldLookup fieldLookup = web.Fields.GetFieldByInternalName(listColumn.InternalName) as SPFieldLookup;
			if (fieldLookup == null)
			{
				FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "'{0}' lookup column not found in '{1}' web", listColumn.InternalName, web.ServerRelativeUrl);
				return;
			}

			fieldLookup.LookupList = spLookupList.ID.ToString();
			fieldLookup.Update(true);
		}

		private static void SetupColumns(SPWeb web)
		{
			// set "Task Content Type" lookup column
			SetColumnCustomProperty(web, SiteColumns.TaskContentTypeLookupColumn,
				Settings.Default.Field_ContentTypeLookup_CustomProperty_BaseContentTypeId_Name,
				ContentTypes.HRTaskContentType.ID.ToString());

			// set "Workflow Position" lookup column
			SetColumnCustomProperty(web, SiteColumns.WorkflowStepOrderColumn,
				Settings.Default.Field_PositionLookup_CustomProperty_ShowDefaultButton_Name, false);
			
			// set "Position" lookup column
			SetColumnCustomProperty(web, SiteColumns.PositionLookupColumn,
				Settings.Default.Field_PositionLookup_CustomProperty_ShowDefaultButton_Name, true);

			// set "Result Content Type" lookup column
			SetColumnCustomProperty(web, SiteColumns.ResultContentTypeLookupColumn,
				Settings.Default.Field_ContentTypeLookup_CustomProperty_BaseContentTypeId_Name,
				ContentTypes.ResultContentType.ID.ToString());

			// set "Tasks List" lookup column
			SetColumnCustomProperty(web, SiteColumns.TasksListLookupColumn,
				Settings.Default.Field_ListLookup_CustomProperty_ContentTypeId_Name,
				ContentTypes.HRTaskContentType.ID.ToString());
		}

		private static void SetColumnCustomProperty(SPWeb web, ListColumn listColumn, string customPropertyName, object value)
		{
			FeatureLogger.Write<MainEventReceiver>(LogLevel.Info, "Set column property: web = {0}, Column = {1}, Property = {2}, Value = {3}",
				web.ServerRelativeUrl, listColumn.InternalName, customPropertyName, value);

			SPField field = web.Fields.TryGetFieldByStaticName(listColumn.InternalName);
			if (field == null)
			{
				FeatureLogger.Write<MainEventReceiver>(LogLevel.Error, "'{0}' field not found in '{1}' web.", listColumn.InternalName, web.ServerRelativeUrl);
				return;
			}

			field.SetCustomProperty(customPropertyName, value);
			field.Update(true);
		}

		#endregion
		
	}
}
